#include <string.h>
#include "unios.h"
#include "ff.h"
#include "ffsystem.h"

/**
 * @brief 获得 FAT 文件系统时间戳
 * @return 时间戳。编码规则如下:
 *     User defined function to give a current time to fatfs module 
 *     31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31)
 *     15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) 
 */
DWORD get_fattime( void ) {
	DWORD stamp;
	int year, mon, day, hour, min, sec;

	if (STD_ERR_OK == unios_rtc_get(&year, &mon, &day, &hour, &min, &sec, NULL)) {
		// sanity checks
		year = (year >= 1980) && (year < 2108) ? year : 1980;
		mon = (mon >= 1) && (mon <= 12) ? mon : 1;
		day = (day >= 1) && (day <= 31) ? day : 1;
		hour = (hour >= 0) && (hour <= 23) ? hour : 0;
		min = (min >= 0) && (min <= 59) ? min : 0;
		sec = (sec >= 0) && (sec <= 59) ? sec : 0;

		stamp = (year - 1980) << 25 | mon << 21 | day << 16 | hour << 11 | min << 5 | sec;
	} else {
		// default time stamp derived from ffconf.h
		stamp = ((DWORD) (FF_NORTC_YEAR - 1980) << 25 | (DWORD) FF_NORTC_MON << 21 | (DWORD) FF_NORTC_MDAY << 16);
	}

	return stamp;
}

void *ff_memalloc (UINT size) {
    return unios_malloc(size);
}

void ff_memfree (void* mf) {
    unios_free(mf);
}

std_err_t ffdisk_mount(uint8_t idx, const char *vol) {
    ffdisk_t *disk = ffdisk_get(idx); if(!disk || !disk->dev) return STD_ERR_NOT_FOUND;
    
    if (FR_OK != f_mount(&disk->fs, vol, 1)) {
    	printf("Failed to mount '%s' ! Try to reformat it and remount ...\n", vol);

		// 格式化
		#if (FF_DEFINED == 80286)
			// FRESULT f_mkfs (
			//     const TCHAR* path,	/* Logical drive number */
			//     BYTE opt,			/* Format option */
			//     DWORD au,			/* Size of allocation unit (cluster) [byte] */
			//     void* work,			/* Pointer to working buffer (null: use heap memory) */
			//     UINT len				/* Size of working buffer [byte] */
			// )
    		FRESULT fres = f_mkfs(vol, FM_EXFAT, 0, NULL, 0);
		#else //if (FF_DEFINED == 86631 || FF_DEFINED == 5380 || FF_DEFINED == 5385)
            // R0.14b(86631), R0.15a(5380), R0.15b(5385)
    		// typedef struct {
    		//     BYTE fmt;			// Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD)
    		//     BYTE n_fat;			// Number of FATs, FAT表数量
    		//     UINT align;			// Data area alignment (sector)
    		//     UINT n_root;			// Number of root directory entries
            //     DWORD au_size;		// 簇(cluster)大小(字节) / Allocation Unit Size, 设置为 0 时会自动根据分区大小计算默认簇大小。
            //                          // 若存储介质有擦除块大小（如Flash），建议 au_size 与擦除块对齐，减少写放大。 例如：Flash擦除块为4KB，则 au_size 设为4KB、8KB等。
    		// } MKFS_PARM;
			MKFS_PARM opt = {.fmt = FM_EXFAT, .n_fat = 1, .align = 0, .n_root = 0, .au_size = disk->au_size };

    		// FRESULT f_mkfs (
    		//     const TCHAR *path,	/* Logical drive number */
    		//     const MKFS_PARM *opt,/* Format options */
    		//     void *work,			/* Pointer to working buffer (null: use len bytes of heap memory) */
    		//     UINT len				/* Size of working buffer [byte] */
    		// )
			FRESULT fres = f_mkfs(vol, &opt, NULL, FF_MAX_SS * 4);
		#endif
        
		if (fres != FR_OK) {
			printf("Unable to format '%s' !\n", vol);
			return STD_ERR_FAIL;
		}

		// 格式化之后，重新挂载
		fres = f_mount(&disk->fs, vol, 1);
		if (fres != FR_OK) {
			printf("Unable to remount '%s' !\n", vol);
			return STD_ERR_FAIL;
		}
    }

    return STD_ERR_OK;
}

//WCHAR ff_oem2uni (WCHAR oem, WORD cp) { return 0; }
//WCHAR ff_uni2oem (DWORD uni, WORD cp) { return 0; }
//DWORD ff_wtoupper (DWORD uni) { return 0; }

static const char *file_rwtest_str = "This is a string more than 32 bytes.\r\n";

std_err_t ffdisk_file_rwtest(const char *fname) {
    FIL fp;
    FRESULT fres;
    char buf[32];
    
    fres = f_open(&fp, fname, FA_WRITE | FA_CREATE_ALWAYS);
    if(fres) return STD_ERR_FAIL;
    fres = f_write(&fp, file_rwtest_str, sizeof(buf), NULL);
    f_close(&fp);
    if(fres != FR_OK) return STD_ERR_FAIL;

    fres = f_open(&fp, fname, FA_READ);
    if(fres) return STD_ERR_FAIL;
    fres = f_read(&fp, buf, sizeof(buf), NULL);
    f_close(&fp);
    if(fres != FR_OK) return STD_ERR_FAIL;

    return memcmp(file_rwtest_str, buf, sizeof(buf)) ? STD_ERR_FAIL : STD_ERR_OK;
}

std_err_t isoc_file_rwtest(const char *fname) {
    FILE *fp;
    char buf[32];
    
    fp = fopen(fname, "w");
    if(!fp) return STD_ERR_FAIL;
    fputs(file_rwtest_str, fp);
    fclose(fp);

    fp = fopen(fname, "r");
    if(!fp) return STD_ERR_FAIL;
    fread(buf, 1, sizeof(buf), fp);
    fclose(fp);
    
    return memcmp(file_rwtest_str, buf, sizeof(buf)) ? STD_ERR_FAIL : STD_ERR_OK;
}
